 /*******************************************************************************
  * Copyright (c) 2000, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.update.internal.core;

 import java.io.File ;
 import java.io.FileInputStream ;
 import java.io.FileOutputStream ;
 import java.io.IOException ;
 import java.io.InputStream ;
 import java.lang.reflect.Method ;
 import java.net.URL ;
 import java.net.URLClassLoader ;
 import java.util.StringTokenizer ;

 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IConfigurationElement;
 import org.eclipse.core.runtime.IExtensionRegistry;
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.osgi.util.NLS;
 import org.eclipse.update.core.ContentReference;
 import org.eclipse.update.core.IFeature;
 import org.eclipse.update.core.IFeatureContentConsumer;
 import org.eclipse.update.core.IInstallHandler;
 import org.eclipse.update.core.IInstallHandlerEntry;
 import org.eclipse.update.core.IInstallHandlerWithFilter;
 import org.eclipse.update.core.INonPluginEntry;
 import org.eclipse.update.core.IPluginEntry;
 import org.eclipse.update.core.IVerificationListener;
 import org.eclipse.update.core.InstallMonitor;
 import org.eclipse.update.core.Utilities;
 import org.osgi.framework.Bundle;

 public class InstallHandlerProxy implements IInstallHandlerWithFilter {

     private IFeature feature = null;
     private int type;
     private IInstallHandler handler = null;
     private IStatus savedStatus = null;
     private boolean DEBUG = false;

     private static final String EXT_PLUGIN = "org.eclipse.update.core"; //$NON-NLS-1$
 private static final String UI_PLUGIN = "org.eclipse.ui"; //$NON-NLS-1$
 private static final String EXT_POINT = "installHandlers"; //$NON-NLS-1$
 private Method nonPluginDataAcceptor = null;

     /**
      * A class loader that combines a the org.eclipse.update.core plugin class loader with the
      * org.eclipse.ui class loader (only when UI is active).
      */
     private static class InstallHandlerClassLoader extends URLClassLoader {
         private Bundle updateCore;
         private Bundle eclipseUI;

         public InstallHandlerClassLoader(URL [] classpath) {
             super(classpath);
             updateCore = Platform.getBundle(EXT_PLUGIN);
             eclipseUI = Platform.getBundle(UI_PLUGIN);
             if (eclipseUI != null && eclipseUI.getState() != Bundle.ACTIVE)
                 eclipseUI = null;
         }

         public Class loadClass(String className) throws ClassNotFoundException {
             // First check update core plugin loader, then the eclipse ui plugin loader
 Class c = null;
             try {
                 c = updateCore.loadClass(className);
             } catch (ClassNotFoundException e) {
                 try {
                     if(eclipseUI != null)
                         c = eclipseUI.loadClass(className);
                 } catch (ClassNotFoundException e2) {
                 } finally {
                 }
             } finally {
             }
             if (c != null)
                 return c;
             else
                 return super.loadClass(className);
         }

         public URL getResource(String resName) {
             // First check update core plugin loader, then the eclipse ui plugin loader
 URL u = updateCore.getResource(resName);
             if(u == null && eclipseUI != null)
                 u = eclipseUI.getResource(resName);
                 
             if (u != null)
                 return u;
             else
                 return super.getResource(resName);
         }
     }
     
     public InstallHandlerProxy(
         int type,
         IFeature feature,
         IInstallHandlerEntry entry,
         InstallMonitor monitor)
         throws CoreException {

         initialize(type, feature, entry, monitor);
     }

     /*
      * @see IInstallHandler#initialize
      */
     public void initialize(
         int type,
         IFeature feature,
         IInstallHandlerEntry entry,
         InstallMonitor monitor)
         throws CoreException {

         DEBUG = UpdateCore.DEBUG_SHOW_IHANDLER;
         // validate arguments
 if (feature == null)
             throw new IllegalArgumentException ();
         this.feature = feature;
         this.type = type;

         // check if we have a handler entry specified in the feature.xml
 if (entry == null) {
             if (DEBUG)
                 debug("not specified"); //$NON-NLS-1$
 return; // no handler entry
 }

         String library = entry.getLibrary();
         String handlerName = entry.getHandlerName();
         if (handlerName == null || handlerName.trim().equals("")) { //$NON-NLS-1$
 if (DEBUG)
                 debug("not specified"); //$NON-NLS-1$
 return; // no handler class spacified in entry
 }
         if (DEBUG) {
             debug("handler=" + handlerName); //$NON-NLS-1$
 debug("path= " + library); //$NON-NLS-1$
 }

         // get handler instance
 try {
             if (library == null || library.trim().equals("")) //$NON-NLS-1$
 this.handler = getGlobalHandler(handlerName);
             else
                 this.handler = getLocalHandler(library, handlerName);
             if (this.handler == null)
                 return;
             handler.initialize(type, feature, entry, monitor);
         } catch (ClassNotFoundException e) {
             handleExceptionInInit(
                 NLS.bind(Messages.InstallHandler_notFound, (new String [] { feature.getLabel() })),
                 e);

         } catch (ClassCastException e) {
             handleExceptionInInit(
                 NLS.bind(Messages.InstallHandler_invalidHandler, (new String [] { feature.getLabel() })),
                 e);
         } catch (CoreException e) {
             handleExceptionInInit(null, e);
         } catch (Exception e) {
             handleExceptionInInit(
                 NLS.bind(Messages.InstallHandler_unableToCreateHandler, (new String [] { feature.getLabel() })),
                 e);
         }

     }

     /*
      * @see IInstallHandler#installInitiated
      */
     public void installInitiated() throws CoreException {
         if (handler == null)
             return;
         else {
             try {
                 if (DEBUG)
                     debug("calling installInitiated()"); //$NON-NLS-1$
 handler.installInitiated();
             } catch (Throwable e) {
                 handleExceptionInCall(e, feature);
             }
         }
     }

     /*
      * @see IInstallHandler#allPluginsDownloaded
      */
     public void pluginsDownloaded(IPluginEntry[] plugins) throws CoreException {
         if (handler == null)
             return;
         else {
             try {
                 if (DEBUG)
                     debug("calling pluginsDownloaded()"); //$NON-NLS-1$
 handler.pluginsDownloaded(plugins);
             } catch (Throwable e) {
                 handleExceptionInCall(e, feature);
             }
         }
     }

     /*
      * @see IInstallHandler#allPluginsInstalled
      */
     public void completeInstall(IFeatureContentConsumer consumer)
         throws CoreException {
         if (handler == null)
             return;
         else {
             try {
                 if (DEBUG)
                     debug("calling completeInstall()"); //$NON-NLS-1$
 handler.completeInstall(consumer);
             } catch (Throwable e) {
                 handleExceptionInCall(e, feature);
             }
         }
     }

     /*
      * @see IInstallHandler#allDataDownloaded
      */
     public void nonPluginDataDownloaded(
         INonPluginEntry[] nonPluginData,
         IVerificationListener listener)
         throws CoreException {
         if (handler == null)
             return;
         else {
             try {
                 if (DEBUG)
                     debug("calling nonPluginDataDownloaded()"); //$NON-NLS-1$
 handler.nonPluginDataDownloaded(nonPluginData, listener);
             } catch (Throwable e) {
                 handleExceptionInCall(e, feature);
             }
         }
     }

     /*
      * @see IInstallHandler#installCompleted
      */
     public void installCompleted(boolean success) throws CoreException {
         if (handler == null)
             return;
         else {
             try {
                 if (DEBUG)
                     debug("calling installCompleted()"); //$NON-NLS-1$
 handler.installCompleted(success);
             } catch (Throwable e) {
                 handleExceptionInCall(e, feature);
             }
         }
     }

     /*
      * @see IInstallHandler#configureInitiated
      */
     public void configureInitiated() throws CoreException {
         if (handler == null)
             return;
         else {
             try {
                 if (DEBUG)
                     debug("calling configureInitiated()"); //$NON-NLS-1$
 handler.configureInitiated();
             } catch (Throwable e) {
                 handleExceptionInCall(e, feature);
             }
         }
     }

     /*
      * @see IInstallHandler#completeConfigure
      */
     public void completeConfigure() throws CoreException {
         if (handler == null)
             return;
         else {
             try {
                 if (DEBUG)
                     debug("calling completeConfigure()"); //$NON-NLS-1$
 handler.completeConfigure();
             } catch (Throwable e) {
                 handleExceptionInCall(e, feature);
             }
         }
     }

     /*
      * @see IInstallHandler#configureCompleted
      */
     public void configureCompleted(boolean success) throws CoreException {
         if (handler == null)
             return;
         else {
             try {
                 if (DEBUG)
                     debug("calling configureCompleted()"); //$NON-NLS-1$
 handler.configureCompleted(success);
             } catch (Throwable e) {
                 handleExceptionInCall(e, feature);
             }
         }
     }

     /*
      * @see IInstallHandler#unconfigureInitiated
      */
     public void unconfigureInitiated() throws CoreException {
         if (handler == null)
             return;
         else {
             try {
                 if (DEBUG)
                     debug("calling unconfigureInitiated()"); //$NON-NLS-1$
 handler.unconfigureInitiated();
             } catch (Throwable e) {
                 handleExceptionInCall(e, feature);
             }
         }
     }

     /*
      * @see IInstallHandler#completeUnconfigure
      */
     public void completeUnconfigure() throws CoreException {
         if (handler == null)
             return;
         else {
             try {
                 if (DEBUG)
                     debug("calling completeUnconfigure()"); //$NON-NLS-1$
 handler.completeUnconfigure();
             } catch (Throwable e) {
                 handleExceptionInCall(e, feature);
             }
         }
     }

     /*
      * @see IInstallHandler#unconfigureCompleted
      */
     public void unconfigureCompleted(boolean success) throws CoreException {
         if (handler == null) {
             if (savedStatus == null)
                 return;
             else
                 throw new CoreException(savedStatus); // delayed exception
 } else {
             try {
                 if (DEBUG)
                     debug("calling unconfigureCompleted()"); //$NON-NLS-1$
 handler.unconfigureCompleted(success);
             } catch (Throwable e) {
                 handleExceptionInCall(e, feature);
             }
             if (savedStatus != null)
                 throw new CoreException(savedStatus); // delayed exception
 }
     }

     /*
      * @see IInstallHandler#uninstallInitiated
      */
     public void uninstallInitiated() throws CoreException {
         if (handler == null)
             return;
         else {
             try {
                 if (DEBUG)
                     debug("calling uninstallInitiated()"); //$NON-NLS-1$
 handler.uninstallInitiated();
             } catch (Throwable e) {
                 handleExceptionInCall(e, feature);
             }
         }
     }

     /*
      * @see IInstallHandler#completeUninstall
      */
     public void completeUninstall() throws CoreException {
         if (handler == null)
             return;
         else {
             try {
                 if (DEBUG)
                     debug("calling completeUninstall()"); //$NON-NLS-1$
 handler.completeUninstall();
             } catch (Throwable e) {
                 handleExceptionInCall(e, feature);
             }
         }
     }

     /*
      * @see IInstallHandler#uninstallCompleted
      */
     public void uninstallCompleted(boolean success) throws CoreException {
         if (handler == null) {
             if (savedStatus == null)
                 return;
             else
                 throw new CoreException(savedStatus); // delayed exception
 } else {
             try {
                 if (DEBUG)
                     debug("calling uninstallCompleted()"); //$NON-NLS-1$
 handler.uninstallCompleted(success);
             } catch (Throwable e) {
                 handleExceptionInCall(e, feature);
             }
             if (savedStatus != null)
                 throw new CoreException(savedStatus); // delayed exception
 }
     }

     /*
      * common exception handling for initialization
      */
     private void handleExceptionInInit(String s, Exception e)
         throws CoreException {

         CoreException ce;
         if (e instanceof CoreException)
             ce = (CoreException) e;
         else
             ce = Utilities.newCoreException(s, e);

         if (isUndoAction()) {
             // for "undo" operations, deactivate handler and log error
 String id =
                 UpdateCore.getPlugin().getBundle().getSymbolicName();
             IStatus status =
                 new Status(IStatus.ERROR, id, 0, "InstallHandler.deactivated", ce); //$NON-NLS-1$
 UpdateCore.getPlugin().getLog().log(status);
             handler = null; // disable subsequent handler calls
 savedStatus = status;
         } else
             // for "do" operations, hurl ...
 throw ce;
     }

     /*
      * common exception handling for calls to install handler
      */
     private void handleExceptionInCall(Throwable e, IFeature feature)
         throws CoreException {

         CoreException ce;
         if (e instanceof CoreException)
             ce = (CoreException) e;
         else
             ce =
                 Utilities.newCoreException(
                     NLS.bind(Messages.InstallHandler_callException, (new String [] { feature.getLabel() })),
                     e);
         
         if (isUndoAction()) {
             // for "undo" operations, deactivate handler and log error
 String id =
                 UpdateCore.getPlugin().getBundle().getSymbolicName();
             IStatus status =
                 new Status(IStatus.ERROR, id, 0, "InstallHandler.deactivated", ce); //$NON-NLS-1$
 UpdateCore.getPlugin().getLog().log(status);
             handler = null; // disable subsequent handler calls
 savedStatus = status;
         } else
             // for "do" operations, hurl ...
 throw ce;
     }

     /*
      * Indicates whether we are doing (install, configure) or
      * undoing (uninstall, unconfigure)
      */
     private boolean isUndoAction() {
         if (this.type == IInstallHandler.HANDLER_ACTION_INSTALL
             || this.type == IInstallHandler.HANDLER_ACTION_CONFIGURE)
             return false; // causes exception to be thrown and action aborted
 else
             return true; // causes exception to be logged and action continues
 }

     /*
      * get an instance of handler downloaded as part of the feature
      */
     private IInstallHandler getLocalHandler(String libs, String name)
         throws IOException , CoreException, ClassNotFoundException , InstantiationException , IllegalAccessException {

         // Get baseline URL for handler (relative to feature.xml). For
 // features being installed from a server (eg. http protocol)
 // the URL will most likely be to a local file copy containing the
 // unpacked feature jar.
 ContentReference baseRef =
             feature.getFeatureContentProvider().getFeatureManifestReference(null);
         URL base = null;
         if (baseRef != null)
             base = baseRef.asURL();
         if (base == null)
             throw Utilities.newCoreException(
                 NLS.bind(Messages.InstallHandler_unableToCreateHandler, (new String [] { this.feature.getLabel() })),
                 null);


         // determine loader class path
 StringTokenizer libraries = new StringTokenizer (libs, ","); //$NON-NLS-1$
 URL [] cp = new URL [libraries.countTokens()];
         for( int token = 0; token < cp.length; token++) {
             cp[token] = new URL (base, libraries.nextToken());
         }
         if (this.type == IInstallHandler.HANDLER_ACTION_UNINSTALL) {
             // check if we are doing uninstall
 // ... need to make temp copy of library (being removed)
 URL [] jars = new URL [cp.length];
             for( int jar = 0; jar < cp.length; jar++) {
                 File tempLib = File.createTempFile("tmp" + jar, ".jar"); //$NON-NLS-1$ //$NON-NLS-2$
 tempLib.deleteOnExit();
                 FileOutputStream fos = null;
                 InputStream is = null;
                 try {
                     fos = new FileOutputStream (tempLib);
                     is = new FileInputStream (cp[jar].getPath());
                     Utilities.copy(is, fos, null);
                 } finally {
                     if (fos != null)
                         try {
                             fos.close();
                         } catch (Exception e) {
                         }
                     if (is != null)
                         try {
                             is.close();
                         } catch (Exception e) {
                         }
                 }
                 jars[jar] = tempLib.toURL();
             }
             cp = jars;
         }

         // create class loader, load and instantiate handler
 ClassLoader loader = new InstallHandlerClassLoader(cp);
         Class clazz = loader.loadClass(name);
         IInstallHandler handler = (IInstallHandler) clazz.newInstance();
         return handler;
     }

     /*
      * get instance of global handler registered via extension point
      */
     private IInstallHandler getGlobalHandler(String name) throws Exception {

         IExtensionRegistry reg = Platform.getExtensionRegistry();
         IConfigurationElement[] handlerExtension =
             reg.getConfigurationElementsFor(EXT_PLUGIN, EXT_POINT, name);
         if (handlerExtension == null || handlerExtension.length <= 0)
             throw Utilities.newCoreException(
                 NLS.bind(Messages.InstallHandler_unableToCreateHandler, (new String [] { this.feature.getLabel() })),
                 null);

         return (IInstallHandler) handlerExtension[0].createExecutableExtension("class"); //$NON-NLS-1$
 }
     
     private void debug(String s) {
         String pfx = (feature==null) ? "" : feature.getVersionedIdentifier().toString(); //$NON-NLS-1$
 System.out.println("InstallHandler["+pfx+"]: " + s); //$NON-NLS-1$ //$NON-NLS-2$
 }

     public boolean acceptNonPluginData(INonPluginEntry data) {
         Boolean result = new Boolean (true);
         if (handler != null){
             if (DEBUG)
                 debug("calling acceptNonPluginData()"); //$NON-NLS-1$
 if(handler instanceof IInstallHandlerWithFilter)
                 return ((IInstallHandlerWithFilter)handler).acceptNonPluginData(data);
             else{ //support upgrade from legacy versions
 if(getNonPluginDataAcceptor() != null){
                     try{
                         Object [] param = {data};
                         result = (Boolean )getNonPluginDataAcceptor().invoke(handler,param);
                     }catch(Exception e){
                         //todo
 }
                 }
             }
         }
         return result.booleanValue();
     }
     private Method getNonPluginDataAcceptor(){
         if(nonPluginDataAcceptor == null){
             try{
                 Class [] types = {INonPluginEntry.class};
                 nonPluginDataAcceptor = handler.getClass().getMethod("acceptNonPluginData",types); //$NON-NLS-1$
 }catch(NoSuchMethodException nsme){
             }
         }
         return nonPluginDataAcceptor;
     }
 }

